package com.echo.im.engin.netty.handler;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.echo.im.common.contant.Constants;
import com.echo.im.common.model.session.IMSessionInfo;
import com.echo.im.engin.packet.LoginSuccessPacket;
import com.echo.im.engin.event.entity.UserOnlineEvent;
import com.echo.im.engin.netty.context.ChannelAttrKey;
import com.echo.im.engin.packet.LoginRequestPacket;
import com.echo.im.infrastructure.session.UserChannelMap;
import com.echo.im.infrastructure.util.SpringContextHolder;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;

/**
 * @author: echo
 * @time: 2024-08-23
 */
@Slf4j
public class LoginHandler extends SimpleChannelInboundHandler<LoginRequestPacket> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, LoginRequestPacket packet) throws Exception {
        //1. 校验token
        IMSessionInfo sessionInfo;
        try {
            JSONObject jsonObject = (JSONObject) StpUtil.getTokenSessionByToken(packet.getToken()).get(Constants.TOKEN_SESSION_NAME);
            sessionInfo = jsonObject.toJavaObject(IMSessionInfo.class);
        }catch (Exception e){
            ctx.close();
            log.warn("用户token校验不通过，强制下线,token:{}", packet.getToken());
            return;
        }
        if (sessionInfo == null){
            ctx.close();
            log.warn("用户token校验不通过，强制下线,token:{}", packet.getToken());
            return;
        }

        //验证成功。移出当前handler
        ctx.channel().pipeline().remove(this);

        // 2. 得到用户信息
        Long userId = sessionInfo.getUserId();
        Integer terminal = sessionInfo.getTerminal();

        // 3. 不允许同设备多源登录
        Channel channel = UserChannelMap.getChannel(userId, terminal);
        if (channel != null && !ctx.channel().id().equals(channel.id())) {
            // 不允许多地登录,强制下线
            channel.close();
            log.info("异地登录，强制下线,userId:{}", userId);
        }

        //4. 绑定用户信息
        UserChannelMap.addChannel(userId, terminal, ctx.channel());
        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
        ctx.channel().attr(userIdAttr).set(userId);
        AttributeKey<Object> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
        ctx.channel().attr(terminalAttr).set(terminal);

        //5. 发布用户上线事件
        ApplicationEventPublisher applicationEventPublisher = SpringContextHolder.getApplicationEventPublisher();
        applicationEventPublisher.publishEvent(new UserOnlineEvent(userId,terminal));

        //6. 初始化心跳次数
        AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf(ChannelAttrKey.HEARTBEAT_TIMES);
        ctx.channel().attr(heartBeatAttr).set(0L);

        //7. 响应ws
        LoginSuccessPacket loginSuccessPacket = new LoginSuccessPacket();
        //随机数: 证明唯一会话 此会话中保证消息的时序性，并有助于ack连续确认
        loginSuccessPacket.setConnectionRandom(RandomUtil.randomInt(0, 999));
        ctx.channel().writeAndFlush(loginSuccessPacket);

    }
}
